﻿using HAF.Dispatcher;
using HAF.Dispatcher.Enum;
using HAF.Dispatcher.Impl;
using HAF.Dispatcher.WorkItem;
using HAF.FtpUtility;
using HAF.FtpUtility.Model;
using HAF.LogUtility.Assistantor;
using HAF.LogUtility.Enum;
using HAF.LogUtility.Except;
using HAF.LogUtility.Model;
using HAF.NetUtility;
using Iptv.ResourceDowner.Helper;
using Iptv.ResourceDowner.Model;
using RDS.Core;
using RDS.Core.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.Remoting.Messaging;
using System.Threading;

namespace Iptv.ResourceDowner
{
    /// <summary>
    /// 介质下载工作项
    /// </summary>
    public class DownWorkerItem : BaseWorkerItem, IPluginRegistable, IDisposable
    {
        #region 插件接口实现
        /// <summary>
        /// 插件自注册
        /// </summary>
        /// <param name="id">工作项编号</param>
        /// <param name="objTask">执行任务</param>
        /// <returns></returns>
        public bool Regist(string id, object objTask)
        {
            #region 入口参数校验
            if (string.IsNullOrEmpty(id))
            {
                WriteDebugMessage("金沙引擎：方法{0}数据解析错误：{1}", System.Reflection.MethodBase.GetCurrentMethod().Name, "参数ID为空");
                return false;
            }
            if (objTask == null)
            {
                WriteDebugMessage("金沙引擎：方法{0}数据解析错误：{1}", System.Reflection.MethodBase.GetCurrentMethod().Name, "参数obj为空");
                return false;
            }
            #endregion 入口参数校验
            try
            {
                var downJob = new DownWorkerItem()
                {
                    ID = int.Parse(TimeHelper.GetTimeStamp()),
                    TaskID = id, //Guid.NewGuid().ToString("N"),
                    Name = string.Concat("DownRes", id),
                    Priority = 10,
                    Model = EnumItemModel.Multi,
                    RedoTimes = 2,
                    WorkID = _defLogName,
                    WorkJson = JsonAssistant.Serialize(objTask),
                    AllowRemoteExec = false,
                    WriteConsoleMessageEvent = RuntimeAdapter.ShowConsol,
                    WorkItems = objTask,
                    RunLogLevel = HAF.LogUtility.Enum.EnumLogLevel.All
                };
                Dispatcher.GetInstance().RegistTaskItem(downJob);

                UpdateTopTaskState(id, 1);

                //RuntimeControler.Tasks.Add(id, downJob);
                WriteDebugMessage(string.Format("金沙引擎：创建{0}工作项完成，下载资源请求已加入执行队列", id));
                return true;
            }
            catch (Exception ex)
            {
                WriteErrMessage(string.Format("金沙引擎：方法{0}发生异常：{1}", System.Reflection.MethodBase.GetCurrentMethod().Name, ex.ToString()));
                return false;
            }
            
        }
        #endregion 接口实现

        #region 私有成员
        const string _defLogName = "SysMonitor";
        string dateStr;
        /// <summary>
        /// 当前运行站点的配置
        /// </summary>
        private SitesConfig _runSiteConfig;
        /// <summary>
        /// 下载队列文件
        /// </summary>
        readonly List<SiteFileStruct> _downQueueFiles = new List<SiteFileStruct>();
        /// <summary>
        /// 对外开放的代理函数
        /// </summary>
        /// <param name="reslst"></param>
        /// <returns></returns>
        public delegate bool GetHandler(SiteTaskConfig reslst);
        /// <summary>
        /// 网盘配置参数
        /// </summary>
        private string _netDiskKey = string.Empty;
        private string loadConfigPath = string.Empty;
        
        private readonly List<SiteTaskConfig> _runTaskCfg = new List<SiteTaskConfig>();
        #endregion 私有成员

        #region 前端任务处理
        /// <summary>
        /// 更新前端任务列表基础数据
        /// </summary>
        /// <param name="correlateId"></param>
        /// <param name="tag"></param>
        /// <param name="siteName"></param>
        private void UpdateTopTaskInfo(string correlateId, string tag, string siteName)
        {
            //通过任务编号找到缓存中执行的任务数据
            var runTaskItem = RuntimeAdapter.Tasks.Find(t => t.ID == TaskID);
            if (runTaskItem != null)
            {
                runTaskItem.Comment = correlateId; // itemCfg.FileSets[0].Group;
                runTaskItem.Tag = correlateId; // itemCfg.Tag;
                runTaskItem.Name = siteName; // itemCfg.Site;
            }
        }

        /// <summary>
        /// 更新前端任务状态
        /// </summary>
        /// <param name="taskid"></param>
        /// <param name="value"></param>
        private void UpdateTopTaskState(string taskid, int value)
        {
            var runTaskItem = RuntimeAdapter.Tasks.Find(t => t.ID == taskid);
            if (runTaskItem != null)
            {
                runTaskItem.State = value;
            }
            //RuntimeAdapter.Tasks.Find(t => t.ID == TaskID).State = value;
        }
        /// <summary>
        /// 结束任务
        /// </summary>
        /// <param name="taskid"></param>
        /// <param name="value"></param>
        private void EndTopTask(string taskid, int value)
        {
            //两种方式写前台监控队列的值
            RuntimeAdapter.Tasks.ForEach(delegate (RunTask t) { if (t.ID == TaskID) { t.State = value; t.EndTime = DateTime.Now; } });
            //RuntimeAdapter.Tasks.Find(t => t.ID == TaskID).State = responseMsg.Code;
            //RuntimeAdapter.Tasks.Find(t => t.ID == TaskID).EndTime = DateTime.Now;
        }
        #endregion 前端任务处理

        /// <summary>
        /// 执行工作项
        /// </summary>
        public override void ExecItem()
        {
            base.ExecItem();
            
            ReportMessageEvent = PushLog;
            OutMessage.ID = _defLogName;
            //更新任务状态为启动中
            UpdateTopTaskState(TaskID, 2);
            //RuntimeAdapter.Tasks.Find(t => t.ID == TaskID).State = 2;
            //获取本地配置文件 每次执行的时候读取，实现热配置
            loadConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, RuntimeAdapter.PluginDir, "sp.json");
            _runSiteConfig = JsonAssistant.FromFile<SitesConfig>(loadConfigPath);
            if (_runSiteConfig == null || _runSiteConfig.ResConfig == null)
            {
                //给请求方应答
                NotifyTaskErr("金沙引擎：工作项外部配置文件加载失败，请与管理员联系！", 8);
                return;
            }
            if (!string.IsNullOrEmpty(WorkJson))
            {
                _runTaskCfg.Clear();
                //提取请求方输入参数
                List<DownResourceInfo> resDownlist = JsonAssistant.Deserialize<List<DownResourceInfo>>(WorkJson);
                if (resDownlist != null && resDownlist.Count > 0)
                {
                    //使用输入的资源下载数据组装下载任务
                    foreach (var item in resDownlist)
                    {
                        CreateDownTask(item);
                    }

                    if (_runTaskCfg.Count > 0)
                    {
                        //TO-DO: IP可访问性判断
                        foreach (var itemCfg in _runTaskCfg)
                        {
                            bool isReady = true;
                            //网络盘符准备
                            if (itemCfg.Controller.AllowNetDisK)
                            {
                                WriteDebugMessageExt(itemCfg.SerialNum, "开始校验网络盘符");
                                InitNetDisk(itemCfg);
                                WriteDebugMessageExt(itemCfg.SerialNum, "网络盘符校验完成");
                                isReady = RuntimeAdapter.NetDisks.ContainsKey(_netDiskKey) && RuntimeAdapter.NetDisks[_netDiskKey];
                                WriteDebugMessageExt(itemCfg.SerialNum, isReady.ToString());
                            }

                            if (isReady)
                            {
                                PushDebugLog(itemCfg.SerialNum, string.Format("工作项{0}开始执行", TaskID));
                                //_dateStr = Path.Combine(DateTime.Now.ToString("yyyyMM"), DateTime.Now.ToString("dd"));

                                //先约定最开始是成功的
                                OutMessage.Code = 0;
                                //OutMessage.Message = "下载任务正常完成！";

                                //使用异步模式处理
                                var handler2 = new GetHandler(DownloadPrepare);
                                IAsyncResult result2 = handler2.BeginInvoke(itemCfg, new AsyncCallback(JobCallBack), null);

                                this.Statu = EnumRunItemStatu.Exec;
                            }
                            else
                            {
                                PushErrorLog(itemCfg.SerialNum, string.Format("插件初始化资源失败，工作项{0}执行中断", TaskID));
                                //给请求方应答
                                NotifyTaskErr("插件初始化资源失败！", 8);
                            }
                        }
                    }
                    else
                    {
                        ShowErrMessage("任务{0}未找到可执行的任务数据", TaskID);
                    }
                }
            }
            else
            {
                ShowErrMessage("任务{0}未找到可执行的任务配置数据", TaskID);
            }
            this.Statu = EnumRunItemStatu.End;
        }

        #region 任务准备
        private bool CheckNetworkable(SiteTaskConfig siteData)
        {
            bool isNetable = false;
            var ipData = long.Parse(siteData.ID);
            ShowDebugMessageExt(siteData.SerialNum, "检查站点[{1}]{0}的可达性", siteData.Host, ipData);
            try
            {
                //站点可达性处理 此处有隐患，如果网络出故障后恢复，也会一直报异常 方案：对异常的网络可不定时检查
                if (!RuntimeAdapter.RegistHosts.ContainsKey(ipData))
                {
                    isNetable = NetStatusHelper.CheckHostPortStatus(siteData.Host, siteData.Port);
                    ShowDebugMessageExt(siteData.SerialNum, "IP{0}可达性为 {1}", siteData.ID, isNetable);
                    RuntimeAdapter.RegistHosts.Add(ipData, isNetable);
                }

                if (RuntimeAdapter.RegistHosts.ContainsKey(ipData) && !RuntimeAdapter.RegistHosts[ipData])
                {
                    UpdateTopTaskState(TaskID, -1);
                    PushErrorLog(siteData.SerialNum, string.Format("RDS主机{2}到远端主机{0} {1}不可达，请检查网络", siteData.Host, siteData.Port, RuntimeAdapter.HostAddress));
                    //给请求方应答
                    NotifyTaskErr(string.Format("RDS主机{2}到远端主机{0} {1}不可达，请检查网络", siteData.Host, siteData.Port, RuntimeAdapter.HostAddress), 7);
                }
            }
            catch (Exception ex)
            {
                WriteDebugMessageExt(siteData.SerialNum, "站点可达性检查异常：{0}", ex.ToString());
            }
            
            return isNetable;
        }
        /// <summary>
        /// 创建下载任务
        /// </summary>
        /// <param name="resourceInfo"></param>
        private void CreateDownTask(DownResourceInfo resourceInfo)
        {
            if (resourceInfo == null)
            {
                return;
            }
            ShowDebugMessageExt(resourceInfo.CorrectID, "添加下载任务{0}到执行队列", resourceInfo.ResFtpAddr);
            OutMessage.ID = resourceInfo.CorrectID;
            //通过Tag在配置文件中获取当前SP配置数据
            LoadConfig spConfig = _runSiteConfig.ResConfig.Find(t => t.Tag.ToLower() == resourceInfo.Tag.ToLower());
            if (spConfig == null)
            {
                UpdateTopTaskState(TaskID, -1);
                //日志上报
                PushErrorLog(resourceInfo.CorrectID, string.Format("未找到标记为{0}的对应配置数据，请检查配置文件{1}", resourceInfo.Tag, loadConfigPath));
                //给请求方应答
                NotifyTaskErr(string.Format("未找到标记为{0}的对应配置数据，请检查配置文件{1}", resourceInfo.Tag, loadConfigPath), 8);
                return;
            }
            //更新界面展示任务基本信息
            UpdateTopTaskInfo(resourceInfo.CorrectID, resourceInfo.Tag, spConfig.Name);
            //解析FTP地址
            var tmpSiteParams = FtpUrlParser.ParseFtpUrl(resourceInfo.ResFtpAddr);
            if (tmpSiteParams != null)
            {
                tmpSiteParams.SerialNum = resourceInfo.CorrectID;
                if (spConfig.CheckNetable)
                {
                    //网络链路检查
                    if (!CheckNetworkable(tmpSiteParams))
                    {
                        UpdateTopTaskState(resourceInfo.CorrectID, 7);
                        return;
                    }
                }
                else
                {
                    ShowDebugMessageExt(resourceInfo.CorrectID, "跳过网络链路检查");
                }

                tmpSiteParams.Tag = resourceInfo.Tag;
                //记录流水号
                if (tmpSiteParams.FileSets != null && tmpSiteParams.FileSets.Count > 0)
                {
                    tmpSiteParams.FileSets[0].Group = resourceInfo.CorrectID;
                    tmpSiteParams.FileSets[0].Tag = resourceInfo.Tag;
                    tmpSiteParams.FileSets[0].TaskDate = resourceInfo.CreateDate;
                    tmpSiteParams.FileSets[0].Status = 0;
                }

                //判断是否已经存在相同的站点数据（IP） 相同站点并且账号相同的数据加入同一个站点任务
                if (_runTaskCfg.Exists(t => t.ID == tmpSiteParams.ID && t.UserId.Equals(tmpSiteParams.UserId)))
                {
                    //将当前下载文件记录加入任务列表
                    _runTaskCfg.Find(t => t.ID == tmpSiteParams.ID && t.UserId.Equals(tmpSiteParams.UserId)).FileSets.AddRange(tmpSiteParams.FileSets);
                }
                else
                {
                    #region 任务参数组装
                    if (tmpSiteParams.Controller == null)
                    {
                        tmpSiteParams.Controller = new SiteControlConfig();
                    }
                    tmpSiteParams.Controller.FileNameblur = spConfig.FileNameblur;
                    tmpSiteParams.Controller.AllowCoverExistsFile = spConfig.ExistsNeedDown;
                    tmpSiteParams.Controller.AllowMD5 = spConfig.AllowMD5;
                    tmpSiteParams.SiteCaption = spConfig.Name;
                    tmpSiteParams.Controller.FileFormats = spConfig.FileFormats;
                    tmpSiteParams.Controller.RedoTimes = spConfig.RedoTimes;
                    tmpSiteParams.Controller.AllowNetDisK = spConfig.AllowNetDisK;
                    tmpSiteParams.Controller.ResouceNetDiskConfig = spConfig.ResouceNetDiskConfig;
                    tmpSiteParams.Controller.ResSaveAddr = spConfig.ResSaveAddr;

                    if (tmpSiteParams.Flow == null)
                    {
                        tmpSiteParams.Flow = new SiteFlowConfig();
                    }
                    tmpSiteParams.Flow.AllowMutliDownThread = spConfig.AllowMutliDownThread;
                    tmpSiteParams.Flow.MaxThreadCount = spConfig.MaxThreadCount;
                    tmpSiteParams.Flow.AllowDownSub = spConfig.AllowDownSub;
                    tmpSiteParams.Flow.AllowSourcePath = spConfig.AllowSourcePath;
                    tmpSiteParams.Flow.PlayUrlTemplate = spConfig.PlayUrlTemplate;
                    tmpSiteParams.Flow.AuditType = spConfig.AuditType;
                    //tmpSiteParams.AllowSourcePath = spConfig.AllowSourcePath;
                    _runTaskCfg.Add(tmpSiteParams);
                    #endregion 任务参数组装
                }
            }
        }

        private bool waitSubThread = true;

        private string ShowMediaRootDirectory(int resType)
        {
            string tmpType = string.Empty;
            switch (resType)
            {
                case 0:
                    tmpType = "Zip";
                    break;
                case 3:
                    tmpType = "Picture";
                    break;
                case 4:
                    tmpType = "Xml";
                    break;
                case 5:
                    tmpType = "Temp";
                    break;
                default:  //1、2
                    tmpType = "Movie";
                    break;
            }
            return tmpType;
        }
        /// <summary>
        /// 下载准备
        /// </summary>
        /// <param name="taskCfg">当前下载任务集合</param>
        /// <remarks>目前只支持单个下载任务</remarks>
        private bool DownloadPrepare(SiteTaskConfig taskCfg)
        {
            ShowDebugMessage("进入DownloadPrepare");
            try
            {
                if (taskCfg != null && taskCfg.FileSets != null && taskCfg.FileSets.Count > 0)
                {
                    var logTag = taskCfg.FileSets[0].Group;
                    PushInfoLog(logTag, string.Format("启动资源下载任务，当前工作项{1}共有{0}个任务需要执行", taskCfg.FileSets.Count, TaskID));
                    
                    OutMessage.ID = taskCfg.FileSets[0].Group;
                    //流水号相同的在一个组
                    List<SiteFileStruct> tmpDownlst = new List<SiteFileStruct>();
                    //整理下载文件，并加入下载队列
                    foreach (var item in taskCfg.FileSets)
                    {
                        WriteDebugMessageExt(logTag, "当前任务{1}下载完整地址为{0}", item.RemoteFullName, TaskID);

                        #region 存储目录处理
                        //默认目录规范 CPTag + 类型(Movie、Picture、Zip) + 年月 + 日 如：zzxxott\Movie\202411\01
                        //通过文件类型确定根目录 
                        string saveRootDir = ShowMediaRootDirectory(item.ResType);
                        //获取日期目录
                        dateStr = string.Format("\\{0}\\{1}", DateTime.Now.ToString("yyyyMM"), DateTime.Now.ToString("dd"));
                        if (!string.IsNullOrEmpty(item.TaskDate))
                        {
                            DateTime tmpDate = Convert.ToDateTime(item.TaskDate);
                            dateStr = Path.Combine(tmpDate.ToString("yyyyMM"), tmpDate.ToString("dd"));
                        }
                        //本地存储文件全路径
                        //1、判断是否使用源目录结构 不再限制仅密流文件使用源路径，通过参数是否允许源路径配置 图片例外
                        string saveFile = taskCfg.Flow.AllowSourcePath && item.ResType != 3 ? Path.Combine(dateStr, item.RemoteFileName) : Path.Combine(dateStr, item.FileName);
                        //2、加上根目录
                        //本地文件存储路径 \\172.25.8.73\worksheet\FTP\Resource\Voole\Movie\202405\04\da982dd264d2cb6eaa24db4c8ba32b58.mp4
                        item.ResSaveFullName = Path.Combine(taskCfg.Controller.ResSaveAddr, saveRootDir, saveFile);
                        //本地文件保存目录 用于M3U8的TS文件/Tar文件做存储路径 \\172.25.8.73\worksheet\FTP\Resource\Voole\Movie\202405\04\
                        item.ResSaveDirectory = Path.Combine(taskCfg.Controller.ResSaveAddr, saveRootDir);
                        #endregion 存储目录处理

                        bool allowFormats = false;
                        //下载文件格式限制判断 文件格式配置为空或*，均表示不做限制
                        if (taskCfg.Controller.FileFormats != null && taskCfg.Controller.FileFormats.Length > 0)
                        {
                            // && !taskCfg.Controller.FileFormats[0].Equals("*")
                            allowFormats = taskCfg.Controller.FileFormats[0].Equals("*");
                            if (!allowFormats)
                            {
                                foreach (var itemf in taskCfg.Controller.FileFormats)
                                {
                                    if (item.ResExtName.ToLower().Equals(itemf))
                                    {
                                        allowFormats = true;
                                        break;
                                    }
                                }
                            }
                        }

                        tmpDownlst.Add(item);
                        if (allowFormats)
                        {
                            WriteDebugMessageExt(logTag, "当前文件格式为{0}，下载存储目录为{1}", item.ResExtName, item.ResSaveDirectory);
                            item.Status = (int)EnumRunItemStatu.Waiting;
                            item.BEnable = true;
                        }
                        else
                        {
                            item.BEnable = false;
                            item.Status = (int)EnumRunItemStatu.Error;
                            WriteErrMessageExt(logTag, "当前文件格式为{0}，不符合下载格式{1}", item.ResExtName, string.Join(",", taskCfg.Controller.FileFormats));
                        }
                    }
                    _downQueueFiles.AddRange(tmpDownlst);

                    if (tmpDownlst.Count > 0)
                    {
                        //唤起下载线程
                        StartDownResExt(taskCfg);

                        bool IsTaskEnd = false;

                        //当有多个任务存在时，等待所有任务执行完成
                        while (!IsTaskEnd)
                        {
                            foreach (var item in _downQueueFiles)
                            {
                                WriteDebugMessageExt(logTag, "工作项[{0}]子任务[{1}]{2}状态为 {3}", TaskID, item.ID, item.FileName, item.Status);
                            }
                            
                            var tmpTask = _downQueueFiles.FindAll(t => t.Status == (int)EnumRunItemStatu.Begin || t.Status == (int)EnumRunItemStatu.Waiting ||
                            t.Status == (int)EnumRunItemStatu.Exec || t.Status == (int)EnumRunItemStatu.Hangup);
                            IsTaskEnd = tmpTask.Count == 0;
                            //waitSubThread;
                            //不让下载线程回执主线程 暂停60秒
                            Thread.Sleep(60 * 1000);
                        }
                    }

                    ShowInfoMessage("工作项{0}执行完成，总任务数{1}，成功{2}个，失败{3}个", TaskID,
                        _downQueueFiles.Count,
                        _downQueueFiles.FindAll(t => t.Status == (int)EnumRunItemStatu.End).Count,
                        _downQueueFiles.FindAll(t => t.Status == (int)EnumRunItemStatu.Error).Count);

                    tmpDownlst.Clear();
                    tmpDownlst = null;
                    #region 过时的处理流程
                    //WriteDebugLog(item.CorrectID, string.Format("开始下载第{0}个资源文件{1}", iNumber, item.ResFtpAddr));
                    //    _stopwathc.Reset();
                    //    _stopwathc.Start();
                    //    _redoTime = 0;
                    //    //先假设下载发生未知错误
                    //    var rtnDown = new MediaDownResult();
                    //    while (_redoTime <= this.RedoTimes)
                    //    {
                    //        if (_redoTime > 0)
                    //        {
                    //            WriteDebugLog(item.CorrectID, string.Format("资源{1}开始第{0}次下载重试", _redoTime + 1, item.ResFtpAddr));
                    //            //WriteDebugMessage(item.CorrectID, string.Format("工单{1}开始第{0}次下载重试", _redoTime, item.CorrectID));
                    //        }
                    //        //执行下载
                    //        rtnDown = StartDownResExt(item);
                    //        //rtnDown = StartDownRes(item);
                    //        WriteDebugLog(item.CorrectID, string.Format("资源{0}第{1}次下载返回值为[{2}]{3}", item.ResFtpAddr, _redoTime + 1, rtnDown.Code, rtnDown.Message));
                    //        item.Code = rtnDown.Code;
                    //        item.Message = rtnDown.Message;
                    //        item.Sign = rtnDown.Sign;
                    //        item.FileSize = rtnDown.FileSize;
                    //        item.Duration = rtnDown.Duration;
                    //        //WriteDebugMessage(item.CorrectID, string.Format("工单{2}第{0}次下载返回值为{1}", _redoTime, rtnCode, item.CorrectID));
                    //        if (rtnDown.Code == (int)EnumFTPServiceCode.Success || (_redoTime == RedoTimes && rtnDown.Code != (int)EnumFTPServiceCode.Success))
                    //        {
                    //            //工单资源下载成功或者已到下载重试最大次数而下载结果不为成功的，则直接跳出循环
                    //            break;
                    //        };
                    //        _redoTime++;
                    //        //延迟处理 6 * 60 * 1000 = 360000 6分钟
                    //        Thread.Sleep(360000);
                    //    }
                    //    _stopwathc.Stop();

                    //    if (rtnDown.Code == (int)EnumFTPServiceCode.Success)
                    //    {
                    //        bool isChecked = false;
                    //        OutMessage.Code = (int)EnumReturnCode.Success;
                    //        OutMessage.Message = string.Empty;
                    //        OutMessage.Token = rtnDown.Sign;

                    //        WriteInfoLog(item.CorrectID, string.Format("工单{3}资源{0}下载完成，共计耗时 {1}分钟（{2} ms)。", item.ResFtpAddr, _stopwathc.ElapsedMilliseconds / 1000 / 60, _stopwathc.ElapsedMilliseconds, item.CorrectID));
                    //        //判断是否需要进行资源MD5校验
                    //        if (item.NeedCheck)
                    //        {
                    //            isChecked = CheckFileByMD5(item);

                    //            WriteInfoLog(item.CorrectID, string.Format("资源文件{0}进行MD5校验判定为{1}", item.ResFtpAddr, isChecked ? "成功" : "失败"));
                    //        }
                    //        else
                    //        {
                    //            isChecked = true;
                    //        }

                    //        if (isChecked)
                    //        {
                    //            //if (item.ResType == EnumResType.Movie)
                    //            //{
                    //            //    ReceiveDBHelper.GetInstance().UpdateMovieDownState(item.CorrectID, item.Code, 1, _localSaveFile);
                    //            //}
                    //            //else if (item.ResType == EnumResType.Picture)
                    //            //{
                    //            //    ReceiveDBHelper.GetInstance().UpdatePicDownState(item.CorrectID, item.Code, 1, _localSaveFile);
                    //            //}
                    //            //else if (item.ResType == EnumResType.EPG)
                    //            //{
                    //            //    ReceiveDBHelper.GetInstance().UpdateEpgReceiveStat(reslst[0].OrderID, (int)EnumC2Stat.DownResourceing);
                    //            //}
                    //        }
                    //        else
                    //        {
                    //            WriteErrorLog(item.CorrectID, string.Format("资源{0}校验失败！", item.ResFtpAddr));
                    //            continue;
                    //        }

                    //    }
                    //    else
                    //    {
                    //        //3次重试后仍然失败
                    //        WriteErrorLog(item.CorrectID, string.Format("资源{0}下载失败，本次下载流程中断", item.ResFtpAddr));
                    //        //失败时直接跳出循环
                    //        break;
                    //        //return false;
                    //    }



                    //if (reslst.Exists(t=> t.Code != (int)EnumFTPServiceCode.Success))
                    //{
                    //    var downItem = reslst.Find(t => t.Code != (int)EnumFTPServiceCode.Success);
                    //    WriteErrorLog(OutMessage.ID, string.Format("资源下载任务执行完成，但有部分资源下载失败，失败原因为：{0}", downItem.Message));
                    //    return false;
                    //}
                    //else
                    //{
                    //    foreach (var item in reslst)
                    //    {
                    //        WriteDebugLog(OutMessage.ID, item.Code.ToString());
                    //    }
                    //    WriteDebugLog(OutMessage.ID, "资源下载任务执行完成");
                    //}
                    #endregion 过时的处理流程
                }
                //return OutMessage.Code == (int)EnumRunItemStatu.End;
                return (_downQueueFiles.FindAll(t => t.Status == (int)EnumRunItemStatu.Error).Count == 0);
            }
            catch (Exception ex)
            {
                PushErrorLog(WorkID, ex.ToString());
                return false;
            }
        }
        #endregion 任务准备

        /// <summary>
        /// 启动下载任务
        /// </summary>
        /// <param name="taskConfig"></param>
        /// <returns></returns>
        private void StartDownResExt(SiteTaskConfig taskConfig)
        {
            //更新所有任务状态为执行中
            //taskConfig.FileSets.ForEach(t => t.Status = (int)EnumRunItemStatu.Exec);
            Thread downMediaThread = new Thread(delegate ()
            {
                ExecPatchDownThread(taskConfig, taskConfig.FileSets, taskConfig.Controller.AllowCoverExistsFile);
            })
            { IsBackground = true, Name = "ExecPatchDownThread" };
            downMediaThread.Start();
        }

        /// <summary>
        /// 下载执行函数
        /// </summary>
        /// <param name="siteConfig">站点配置信息</param>
        /// <param name="resMetas">待下载文件集合</param>
        /// <param name="isCover">是否覆盖已存在的文件</param>
        private void ExecPatchDownThread(SiteLinkParameter siteConfig, List<SiteFileStruct> resMetas, bool isCover)
        {
            try
            {
                //启动下载
                using (FluentFtpHelper ftpClien = new FluentFtpHelper(siteConfig))
                {
                    ftpClien.LogOutName = resMetas[0].Group;
                    ftpClien.WriteConsoleMessageEvent = WriteConsoleMessageEvent;
                    ftpClien.ReportMessageEvent = PushLog;
                    //ftpClien.DownloadProcessChangeEvent = WriteProcessEvnet;
                    //ftpClien.DownloadChangeEvent = DownloadChangeEvent;
                    ftpClien.DownloadEndEvent = GatherEnd;

                    //注意：Path.Combine使用时，后续的地址不能加/或者//
                    //string localPath = Path.Combine(_runConfig.SaveDir, dateStr, cpName);
                    ResponsePackage result = ftpClien.DownPatchFiles(resMetas, resMetas[0].Group);
                    OutMessage.Code = result.Code == (int)TransferCode.Success ? (int)EnumRunItemStatu.End : (int)EnumRunItemStatu.Error;

                    //SetDownStatus(taskNum, result.Code);
                };
            }
            catch (Exception ex)
            {
                //OutMessage.Code = (int)EnumRunItemStatu.Error;
                //OutMessage.Message = ex.ToString();
                WriteErrMessage(ex.ToString());
                NotifyTaskErr(ex.ToString());
            }
            
        }

        object myObject = new object();
        /// <summary>
        /// 设置对象执行状态
        /// </summary>
        /// <param name="fileId"></param>
        /// <param name="stat"></param>
        private void SetDownStatus(string fileId, int stat)
        {
            //多线程处理
            lock (myObject)
            {
                _downQueueFiles.ForEach(delegate (SiteFileStruct t)
                {
                    if (t.ID == fileId)
                    {
                        t.Status = stat;
                    }
                });
            }
        }

        private string responsePlayUrl = string.Empty;

        /// <summary>
        /// 子任务采集完成处理
        /// </summary>
        /// <param name="taskId">下载任务编号</param>
        /// <param name="state">下载任务状态</param>
        private void GatherEnd(string taskId, int state)
        {
            if (_downQueueFiles != null && _downQueueFiles.Count > 0)
            {
                string tmpLogName = _downQueueFiles[0].Group;
                //WriteDebugMessageExt(tmpLogName, "工作项 {2} 介质下载子任务{0}处理完成 [{1}]", taskId, (TransferCode)state, TaskID);
                int i = 1;
                foreach (var item in _downQueueFiles)
                {
                    WriteDebugMessageExt(tmpLogName, "工作项 {1} 子任务{2}编号为： {0} 状态为 {3}",item.ID, TaskID, i, item.Status);
                    i++;
                }
                
                SiteFileStruct tmpfile = _downQueueFiles.Find(t => t.ID == taskId);
                if (tmpfile != null)
                {
                    PushInfoLog(tmpLogName, "工作项 {2} 介质下载子任务{0}处理完成 {3} [{1}]", taskId, (TransferCode)state, TaskID, tmpfile.RemoteFileName);

                    //任务失败直接返回，后续流程不再处理
                    if (state != (int)TransferCode.Success)
                    {
                        tmpfile.Status = -1;
                        OutMessage.Code = (int)TransferCode.Error;
                        OutMessage.Message += string.Format("工单[{0}]下载任务[{1}]{2}失败 {3} ", tmpLogName, TaskID, tmpfile.RemoteFileName, (TransferCode)state);
                        return;
                    }

                    try
                    {
                        //通过Tag在配置文件中获取当前SP配置数据
                        LoadConfig spConfig = _runSiteConfig.ResConfig.Find(t => t.Tag.ToLower() == tmpfile.Tag.ToLower());
                        if (spConfig == null)
                        {
                            throw new RunException(TransferCode.ConfigErr, string.Format("未找到标记为{0}的对应配置数据，请检查配置文件", tmpfile.Tag));
                        }
                        WriteDebugMessageExt(tmpLogName, "任务 {0} 通过后缀名{1}判断处理模式", taskId, tmpfile.ResExtName);
                        //存在着M3U8文件不需要下载ts子文件情况
                        if (tmpfile.ResType == (int)EnumResourceType.Flow && spConfig.AllowDownSub)
                        {
                            //任务挂起，等待子任务执行完成
                            tmpfile.Status = (int)EnumRunItemStatu.Hangup;
                            FlowDownPrepare(tmpfile, spConfig, tmpLogName);
                        }
                        else if (tmpfile.ResType == (int)EnumResourceType.Zip)
                        {
                            //任务挂起，等待子任务执行完成
                            tmpfile.Status = (int)EnumRunItemStatu.Hangup;
                            UnPackPrepare(tmpfile, spConfig, tmpLogName);
                        }
                        else
                        {
                            if (tmpfile.ResType == (int)EnumResourceType.Flow)
                            {
                                if (spConfig.AuditType == 0)
                                {
                                    //对流文件拼接播放地址
                                    string tmpPlayUrl = GetPlayUrl(spConfig.PlayUrlTemplate, spConfig.PlayParams, tmpfile.Name);
                                    responsePlayUrl = string.Format("{0}/{1}/{2}", tmpPlayUrl, tmpfile.RemoteRootDirectory, string.Format("{0}.m3u8", tmpfile.Name));
                                }
                                else
                                {
                                    //ftp://zftp:zftp@182.131.0.201:21/bilibili/iptvsdkmdium/40028/466246795/_00003porn1nqf6bm32vvr7iy03nbfob-drm_slice/video.m3u8
                                    //对流文件拼接播放地址
                                    string tmpPlayUrl = GetPlayUrl(spConfig.PlayUrlTemplate, spConfig.PlayParams, tmpfile.RemoteRootDirectory);
                                    responsePlayUrl = string.Format("{0}/{1}/{2}", tmpPlayUrl, tmpfile.RemoteRootDirectory, "video.m3u8");
                                }
                            }

                            WriteDebugMessageExt(tmpLogName, "工作项{0}任务{1}下载流程结束", TaskID, taskId);
                            PushDebugLog(tmpLogName, "工作项{0}任务{1}下载流程结束", TaskID, taskId);
                            OutMessage.Code = (int)EnumRunItemStatu.End;
                            //OutMessage.Message = "任务正常完成";  //成功的消息最后回执时处理，此处不填写，避免将失败消息替换了 Hansy 2025-1-9

                            tmpfile.Status = state == 200 ? 5 : -1; //(int)EnumRunItemStatu.End
                            return;
                            //若当前工作项有多个任务时，会导致多次回执，而工作项包含的M3U8任务未执行完成即回执了
                            //如果无子任务，则退出循环并进行回执
                            //waitSubThread = false;
                        }

                        //循环等待子任务完成后执行回调
                        while (waitSubThread)
                        {
                            ShowDebugMessageExt(tmpLogName, "工作项{1}等待子任务{0}执行", tmpfile.ID, TaskID);
                            Thread.Sleep(30000); //5 * 60 * 1000;
                        }

                    }
                    catch (Exception ex)
                    {
                        NotifyTaskErr(string.Format("工作项{2}任务{0}执行失败 {1}", taskId, ex.ToString(), TaskID));
                    }
                    finally
                    {
                        //waitSubThread = false;
                    }
                }
                else
                {
                    NotifyTaskErr(string.Format("任务执行失败 {0} 下载队列未检索到数据", TaskID));
                }
            }
            else
            {
                NotifyTaskErr(string.Format("任务{1}执行失败 {0} 下载队列为空", taskId, TaskID));
            }

        }

        #region M3U8处理
        
        private string GetLastLevelDirectory(string rootDir)
        {
            if (string.IsNullOrEmpty(rootDir))
            {
                return "";
            }

            string[] dirs = rootDir.Split('/');
            return dirs[dirs.Length - 1];
        }

        /// <summary>
        /// 压缩包解压准备
        /// </summary>
        private void UnPackPrepare(SiteFileStruct tmpfile, LoadConfig spConfig, string tmpLogName)
        {
            //tmpfile.Name
            string lastDir = GetLastLevelDirectory(tmpfile.RemoteRootDirectory);
            //解压输出类型目录 不包含文件名解压后形成的路径  .Replace("/", "\\")

            string unPackPath = Path.Combine("Movie", dateStr, lastDir);
            string tmpFlowFullName = string.Empty;
            string tmpZipName = Path.GetFileNameWithoutExtension(tmpfile.FileName);
            //解压输出目录
            string unPackDir = Path.Combine(tmpfile.ResSaveDirectory.Replace("\\Zip", ""), unPackPath);
            string tmpPlayUrl = GetPlayUrl(spConfig.PlayUrlTemplate, spConfig.PlayParams, tmpfile.RemoteFileName, tmpLogName);

            //0 腾讯 1 B站 2 爱奇艺
            switch (spConfig.AuditType)
            {
                case 0: //腾讯文件名需要包名 ZipFileName.ts.m3u8
                    tmpFlowFullName = Path.Combine(tmpfile.ResSaveDirectory.Replace("\\Zip", ""), unPackPath, tmpZipName, string.Format("{0}.ts.m3u8", tmpZipName));
                    responsePlayUrl = string.Format("{0}/{1}/{2}/{3}/{4}", tmpPlayUrl, dateStr.Replace("\\", "/"), lastDir, tmpZipName, string.Format("{0}.ts.m3u8", tmpZipName));
                    break;
                case 1:  //B站流文件名称固定为video.m3u8
                    tmpFlowFullName = Path.Combine(tmpfile.ResSaveDirectory.Replace("\\Zip", ""), unPackPath, tmpZipName, "video.m3u8");
                    responsePlayUrl = string.Format("{0}/{1}/{2}/{3}/{4}", tmpPlayUrl, dateStr.Replace("\\", "/"), lastDir, tmpZipName, "video.m3u8");
                    break;
                default:  //直接使用压缩包文件名做流文件名 ZipFileName.m3u8
                    tmpFlowFullName = Path.Combine(tmpfile.ResSaveDirectory.Replace("\\Zip", ""), unPackPath, string.Concat(tmpZipName, ".m3u8"));
                    responsePlayUrl = string.Format("{0}/{1}/{2}/{3}.m3u8", tmpPlayUrl, dateStr.Replace("\\", "/"), lastDir, tmpZipName);
                    unPackDir = Path.Combine(tmpfile.ResSaveDirectory.Replace("\\Zip", ""), "Movie", dateStr);
                    break;
            }

            #region 废弃代码
            /*
            if (spConfig.AuditType == 0) //腾讯模式 取文件名中dot第一个字串为审核播放ID vid @aid
            {
                //规范：压缩包的名称即为m3u8文件的名称（前面还需加一个ts）
                //\\172.25.8.73\worksheet\FTP\Resources\zzxxott\20240511/b0045dzedgi.2.w0045ua2723.321004/b0045dzedgi.2.w0045ua2723.321004.ts.m3u8
                //无扩展名称的文件名 Tar包的文件名 \\172.25.8.73\worksheet\FTP\Resources\zzxxott\Movie\202405\26\b4100wctj3k.2.b4100wctj3k.321004.tar
                tmpFlowFullName = Path.Combine(tmpfile.ResSaveDirectory.Replace("\\Zip",""), unPackPath, tmpZipName, string.Format("{0}.ts.m3u8", tmpZipName));
                //string tmpPlayUrl = GetPlayUrl(spConfig.PlayUrlTemplate, spConfig.PlayParams, tmpfile.RemoteFileName, tmpLogName);
                responsePlayUrl = string.Format("{0}/{1}/{2}/{3}/{4}", tmpPlayUrl, dateStr.Replace("\\","/"), tmpfile.RemoteRootDirectory, tmpZipName, string.Format("{0}.ts.m3u8", tmpZipName));
            }
            else //B站模式 文件名称固定 取目录中的某个字段做播放ID
            {
                //规范：压缩包的名称仅作目录，m3u8文件的名称固定为video.m3u8
                //\\172.25.8.73\worksheet\FTP\Resources\zensott\bilibili/iptvsdkmdium/40156/510905722/_00003l5usaqa3m8sk2sfi9qfhli63kb-drm_slice/video.m3u8
                //ftp://zftp:zftp@182.131.0.201:21/bilibili/iptvsdkmdium/40028/466246795/_00003porn1nqf6bm32vvr7iy03nbfob-drm_slice/video.m3u8
                //http://8.137.34.175/audits/?cid=210288241&url=http://172.25.8.29/zensott/Movie/bilibili/iptvsdkmdium/33802/210288241/_00001vn5wrgufrvrm1zhhwibzyj8t3b-drm_slice/video.m3u8
                //无扩展名称的文件名 zip包的文件名  \\172.25.8.73\worksheet\FTP\Resources\zensott\Movie\202405\24\_00001aspxs9m1mgl72ngakcp66trrdb-drm_slice.zip
                //string tmpZipName = Path.GetFileNameWithoutExtension(tmpfile.FileName);
                //unPackDir = Path.Combine(unPackPath, tmpZipName);
                //unPackPath = unPackDir;
                tmpFlowFullName = Path.Combine(unPackPath, tmpZipName, "video.m3u8");
                //string tmpPlayUrl = GetPlayUrl(spConfig.PlayUrlTemplate, spConfig.PlayParams, tmpfile.RemoteRootDirectory, tmpLogName);
                responsePlayUrl = string.Format("{0}/{1}/{2}/{3}/{4}", tmpPlayUrl, dateStr.Replace("\\", "/"), tmpfile.RemoteRootDirectory, tmpZipName, "video.m3u8");
            }
            */
            #endregion 废弃代码

            ShowDebugMessageExt(tmpLogName, "检查目录{0} 流文件{1} 是否存在", unPackDir, tmpFlowFullName);
            if (Directory.Exists(unPackDir) && File.Exists(tmpFlowFullName))
            {
                PushDebugLog(tmpLogName, "流文件{0}已存在", tmpFlowFullName);
                //TO-DO:根据流文件中TS切片数量判断是否需要下载或者解压
                if (spConfig.AllowDownSub)
                {
                    UnPackZipAct(tmpLogName, tmpfile.ResExtName.ToLower().Equals(".tar") ? 0 : 1 , tmpfile.ResSaveFullName.Replace("/","\\"), unPackDir);
                }
            }
            else
            {
                PushDebugLog(tmpLogName, "未找到流媒体文件{0}，准备启动解压动作", tmpFlowFullName);
                UnPackZipAct(tmpLogName, tmpfile.ResExtName.ToLower().Equals(".tar") ? 0 : 1, tmpfile.ResSaveFullName.Replace("/", "\\"), unPackDir);
            }
            M3u8Helper m3U8Helper = new M3u8Helper
            {
                LogOutName = tmpLogName
            };
            try
            {
                long m3u8Time = m3U8Helper.GetM3u8Data(tmpFlowFullName, out int m3u8Count);
                //在输出目录查找M3U8文件，并保存到下载队列文件中去
                //string flowfilePath = GetM3U8FileFullPath(unPackPath);
                if (string.IsNullOrEmpty(responsePlayUrl))
                {
                    responsePlayUrl = tmpFlowFullName;
                }
                ShowDebugMessageExt(tmpLogName, "播放流文件{0}时长为{1} ts文件数量为{2}", responsePlayUrl, DataTransfer.FormatTimeStr(m3u8Time * 1000), m3u8Count);
            }
            catch (RunException ex)
            {
                WriteDebugMessageExt(tmpLogName, ex.ErrorMessage);
            }

            tmpfile.ResSaveDirectory = tmpFlowFullName;
            OutMessage.Code = (int)EnumRunItemStatu.End;
            //OutMessage.Message = "任务正常完成";
            OutMessage.CmdUrl = responsePlayUrl;

            tmpfile.Status = (int)EnumRunItemStatu.End;
            waitSubThread = false;
            return;
        }
        
        /// <summary>
        /// 解包
        /// </summary>
        /// <param name="taskId">任务编号</param>
        /// <param name="resType">资源类型 zip tar gz</param>
        /// <param name="zipFile">压缩包全路径</param>
        /// <param name="unPackDir">解压目录</param>
        private void UnPackZipAct(string taskId, int resType, string zipFile, string unPackDir)
        {
            WriteDebugMessageExt(taskId, string.Format("启动压缩包解压工作 压缩包源路径 {0} 解压输出目录{1}", zipFile, unPackDir));
            bool zipResult = false;
            Stopwatch _stopwathc = new Stopwatch();
            _stopwathc.Start();
            //压缩包文件的解压操作
            if (resType == 0) //tar包
            {
                zipResult = ZipHelper.UnpackTarFiles(zipFile, unPackDir, taskId);
            }
            else
            {
                zipResult =  ZipHelper.UnpackZipFile(taskId, zipFile, unPackDir);
            }
            _stopwathc.Stop();
            WriteInfoMessageExt(taskId, "工作项{0}任务{1}解压流程结束，共耗时{2} {3}", TaskID, taskId, FileAssistant.GetTimeStr(_stopwathc.ElapsedMilliseconds), zipResult);
        }
        
        /// <summary>
        /// 流媒资下载准备
        /// </summary>
        private void FlowDownPrepare(SiteFileStruct tmpfile, LoadConfig spConfig, string tmpLogName)
        {
            //如果当前文件是M3U8文件并且显示指定下载子数据，则需要下载M3u8列表中的所有ts文件到本地
            //M3U8文件下载切片文件操作
            string tmpLocalPath = Path.Combine(tmpfile.ResSaveDirectory, dateStr, tmpfile.RemoteRootDirectory.Replace("/", "\\"));
            PushInfoLog(tmpLogName, string.Format("启动M3U8子任务下载 site {0} LocalM3U8Path {1} TsSavePath {2} LocalDirectory {3}",
                tmpfile.SiteURI, tmpfile.RemoteRootDirectory, tmpfile.ResSaveDirectory, tmpLocalPath));
            //获取审核播放链接
            string tmpPlayUrl = GetPlayUrl(spConfig.PlayUrlTemplate, spConfig.PlayParams, tmpfile.RemoteFileName, tmpLogName);
            responsePlayUrl = string.Format("{0}/{1}/{2}", tmpPlayUrl, tmpfile.RemoteRootDirectory, tmpfile.FileName);
            ShowDebugMessageExt(tmpLogName, "检查播放流文件{0}是否已正确获取", responsePlayUrl);

            //数据对象组装
            M3u8TaskModel taskData = new M3u8TaskModel()
            {
                Tag = tmpfile.Tag,
                ID = tmpLogName,
                TaskID = tmpfile.ID,
                M3u8Url = tmpfile.RemoteFullName,
                M3u8Path = tmpfile.ResSaveFullName.Replace("/", "\\"),
                LocalDirectory = tmpLocalPath,
                AllowMultiDown = spConfig.AllowMutliDownThread,
                MaxDownThread = spConfig.MaxThreadCount,
                SubUrl = new List<TsEntity>()
            };
            //流媒资ts子介质下载
            using (M3u8TaskAssistant m3U8Task = new M3u8TaskAssistant(taskData))
            {
                LogOutName = WorkID;
                m3U8Task.WriteConsoleMessageEvent = WriteConsoleMessageEvent;
                m3U8Task.ReportMessageEvent = PushLog;
                m3U8Task.TaskEndEvent = SubTaskEnd;
                m3U8Task.Exec();
            }
        }

        /**
        * 掌信密流（腾讯）取一个值 文件名分割符约定用英文句号 "."，不单独配置分割符号
        * {"vid":l00469csvi7} 配置 @vid,0,1 控制参数@vid； 类型 0 文件； 取值序号 1 分离后的第一个数据
        * 原始地址 ftp://zzx_tencent_tar:gqybq!#$88@182.138.25.61/mzc00200b4jsdq6/l00469csvi7.2.l00469csvi7.321004.tar
        * #模板 #http:// open.tv.qq.com/iptvplayer/player?vid=@aid & video_url= http: //172.25.8.29/zzxxott/Movie/
        * http:// open.tv.qq.com/iptvplayer/player?vid=l00469csvi7 & video_url= http: //172.25.8.29/zzxxott/Movie/mzc00200b4jsdq6/l00469csvi7.2.l00469csvi7.321004/l00469csvi7.2.l00469csvi7.321004.ts.m3u8
        * 仁视（B站）
        * {"aid":"113316001221826","sid":"45196","cid":"26436437466","epid":"840457"}
        * ftp:// zftp:Zlg2024%!@182.131.0.201:21/bilibili/iptvsdkmdium/45196/26436437466/_00003t9y02s7d3b692hqsoripoglmdb-drm_slice.zip
        * 欢网（腾讯）
        * 
        * 金胡桃（爱奇艺） 取两个值
        * $tvid/$vid.tar
        * 
        ***/
        /// <summary>
        /// 拼接流文件播放地址
        /// </summary>
        /// <param name="inUrl">播放地址模板字串</param>
        /// <param name="playParams">播控参数集</param>
        /// <param name="inStr">待解析字串</param>
        /// <param name="logName">日志输出名称</param>
        /// <returns></returns>
        private string GetPlayUrl(string inUrl, string[] playParams, string inStr, string logName = "")
        {
            //通过文件名或者目录名获取播控参数，不包括在XML工单中获取
            string outUrl = string.Empty;
            string tmpValue = string.Empty;
            string logStr = inUrl.Replace("@", "&#64;");
            WriteWarnMessageExt(logName, "启动审核播放地址拼接，模板地址为{0}，待解析字串为{1}", logStr, inStr);
            string[] tmpLst;
            if (playParams.Length > 0)
            {
                foreach (var item in playParams)
                {
                    tmpLst = item.Split(',');
                    if (tmpLst.Length == 3)
                    {
                        //参数名称 如@vid @tid 
                        string paramName = tmpLst[0];
                        //参数类型 0 文件 1 目录
                        string paramType = tmpLst[1];
                        //取值位置 目录 1 倒数第一个目录； 文件 正数，如1 文件名通过split取得的第一个值
                        string paramPosition = tmpLst[2];
                        bool rtn = int.TryParse(paramType, out int ptype);
                        if (rtn)
                        {
                            rtn = int.TryParse(paramPosition, out int position);
                            if (rtn)
                            {
                                string[] tmpNameLst;
                                switch (ptype)
                                {
                                    case 0: //取文件名称
                                        //腾讯 b0045dzedgi.2.w0045ua2723.321004.mp4
                                        //获取文件名
                                        string tmpName = Path.GetFileName(inStr);
                                        //使用分隔符获取字符串集合
                                        tmpNameLst = tmpName.Split('.');
                                        //根据位置提取数据
                                        tmpValue = tmpNameLst[position - 1];
                                        break;
                                    default:
                                        //B站 bilibili/iptvsdkmdium/48674/26345735799/_00003w4tj9gc83afk2s1iffagt2eatb-drm_slice.zip
                                        tmpNameLst = inStr.Split('/');
                                        tmpValue = tmpNameLst[tmpNameLst.Length - position];
                                        break;
                                }
                            }
                        }
                        outUrl = inUrl.Replace(paramName, tmpValue);
                    }
                }
            }
            WriteDebugMessageExt(logName, "AID取值为{0}，拼接地址为{1}", tmpValue, outUrl);

            return outUrl;
        }

        /// <summary>
        /// 支线任务结束
        /// </summary>
        private void SubTaskEnd(string taskId, int stat, string message)
        {
            ShowInfoMessage("任务{0}的支线任务执行完成，状态码为{1}，回馈消息为{1}", taskId, stat, message);

            OutMessage.Code = stat;
            OutMessage.Message = message;

            waitSubThread = false;

            _downQueueFiles.ForEach(delegate (SiteFileStruct t)
            {
                if (t.ID == taskId)
                {
                    t.Status = stat == 200 ? 5 : -1;
                }
            });

            
        }
        #endregion M3U8处理

        #region 暂时作废
        /// <summary>
        /// 启动下载任务
        /// </summary>
        /// <param name="resMeta"></param>
        /// <returns></returns>
        private MediaDownResult StartDownRes(DownResourceInfo resMeta)
        {
            //var rtn = new MediaDownResult() { Code = (int)EnumFTPServiceCode.UnKnownErr, Message = "未知错误" };
            //try
            //{
            //    this.WorkID = resMeta.CorrectID;
            //    //1、解析FTPURI信息
            //    FtpConfig ftpCfg = FtpManager.GetConfig(resMeta.ResFtpAddr);
            //    //远端资源文件名
            //    string remotefilename = Path.GetFileName(resMeta.ResFtpAddr);
            //    //资源后缀名
            //    string fileExtName = Path.GetExtension(resMeta.ResFtpAddr);
            //    //WriteInfoLog(resMeta.CorrectID, string.Format("开始下载工单资源文件{0}", resMeta.ResFtpAddr));
            //    //_inC2Params.FileName = fileName;
            //    WriteDebugLog(string.Format("下载资源文件后缀名为{0}", fileExtName));

            //    //根据后缀名确定资源类型，并判断本地存储路径
            //    string resFixDir = string.Empty;
            //    if (fileExtName.ToLower() == ".mp4" || fileExtName.ToLower() == ".ts")
            //    {
            //        resFixDir = "Movie";
            //        resMeta.ResType = EnumResType.Movie;
            //    }
            //    if (fileExtName.ToLower() == ".jpg" || fileExtName.ToLower() == ".png")
            //    {
            //        resFixDir = "Picture";
            //        resMeta.ResType = EnumResType.Picture;
            //    }
            //    if (fileExtName.ToLower() == ".tar")
            //    {
            //        resFixDir = "EPG";
            //        resMeta.ResType = EnumResType.EPG;
            //    }

            //    //本地存储目录
            //    string localDir = Path.Combine(resMeta.ResSaveAddr, resFixDir, _dateStr);
            //    //本地存储文件名
            //    _localSaveFile = Path.Combine(resMeta.ResSaveAddr, resFixDir, _dateStr, remotefilename);
            //    resMeta.LocalResAddr = _localSaveFile;
            //    //_inC2Params.InXml = saveFile;

            //    WriteDebugLog(string.Format("资源下载信息：ServerIP:{0} Port:{1} UserName:{2} PassWord:{3} FileName:{4} SaveFileName:{5}", ftpCfg.IP, ftpCfg.Port,
            //            ftpCfg.UserId, ftpCfg.Password, ftpCfg.RemoteFile, _localSaveFile));

            //    FtpManager ftpManager = new FtpManager(Guid.NewGuid(), ftpCfg.IP, ftpCfg.Port, "", ftpCfg.UserId, ftpCfg.Password);
            //    ftpManager.FileDownloadMessagePushEvent = WriteFtpLog;

            //    WriteInfoLog(string.Format("开始下载资源{0}", resMeta.ResFtpAddr));
            //    bool beginDown = false;
            //    if (resMeta.AllowNetDisK)
            //    {
            //        string saveDir = _localSaveFile.Substring(0, _localSaveFile.LastIndexOf(@"\"));
            //        WriteDebugLog(string.Format("网络目录检查{0}", saveDir));
            //        if (!Directory.Exists(saveDir))
            //        {
            //            WriteInfoLog(String.Format("开始创建下载目录{0}", saveDir));
            //            Directory.CreateDirectory(saveDir);
            //        }
            //        beginDown = true;
            //        //beginDown = CreateNetDirectory(saveDir);
            //    }
            //    else
            //    {
            //        if (!Directory.Exists(localDir))
            //        {
            //            Directory.CreateDirectory(localDir);
            //        }
            //        beginDown = true;
            //    }

            //    if (beginDown)
            //    {
            //        ResponseLog rtnDown;
            //        if (resMeta.AllowMD5)
            //        {
            //            WriteDebugLog(string.Format("当前SP{0}相关资源文件需要进行MD5校验，调用DownloadWithMD5函数进行下载操作", resMeta.SpName));
            //            rtnDown = ftpManager.DownloadWithMD5(_localSaveFile, ftpCfg.RemoteFile);
            //        }
            //        else
            //        {
            //            rtnDown = ftpManager.Download(_localSaveFile, ftpCfg.RemoteFile);
            //        }
            //        rtn.Code = rtnDown.Code;
            //        rtn.Message = rtnDown.Message;
            //        rtn.Sign = rtnDown.Sign;
            //    }
            //    //else
            //    //{
            //    //    JobCallBack(null);
            //    //}

            //    //获取视频文件时长
            //    //if ((rtn.Code == (int)EnumFTPServiceCode.Success) && (resMeta.ResType == EnumResType.Movie))
            //    //{
            //    //    string mediaTime = FileAssistantor.GetMediaTimeLen(_localSaveFile);

            //    //}

            //    //return rtn.Code == 0 ? EnumFTPServiceCode.Success : EnumFTPServiceCode.DownErr;
            //}
            //catch (Exception ex)
            //{
            //    WriteErrorLog(ex.ToString());
            //    rtn.Code = (int)EnumFTPServiceCode.UnKnownErr;
            //    rtn.Message = ex.Message;
            //}
            ////finally
            ////{

            ////}
            //return rtn;
            return null;
        }

        /// <summary>
        /// 多线程下载
        /// </summary>
        /// <param name="reslst">下载任务集合</param>
        /// <returns></returns>
        private bool MutliDowloadResources(List<DownResourceInfo> reslst)
        {
            return false;
        }
        #endregion 暂时作废

        #region NetDisk
        /// <summary>
        /// 网盘初始化加载
        /// </summary>
        /// <param name="diskCfg"></param>
        private void InitNetDisk(SiteTaskConfig diskCfg)
        {
            if (diskCfg.Controller.AllowNetDisK && !string.IsNullOrEmpty(diskCfg.Controller.ResouceNetDiskConfig))
            {
                WriteDebugMessageExt(diskCfg.SerialNum, "当前CP网络盘符参数为{0}", diskCfg.Controller.ResouceNetDiskConfig);
                string[] NetDisKConfig = diskCfg.Controller.ResouceNetDiskConfig.Split(',');
                if (NetDisKConfig.Length > 0)
                {
                    bool isReady = false;
                    _netDiskKey = NetDisKConfig[2];
                    if (RuntimeAdapter.NetDisks != null && RuntimeAdapter.NetDisks.Count > 0)
                    {
                        WriteDebugMessage(RuntimeAdapter.NetDisks.Keys.ToString());
                        if (RuntimeAdapter.NetDisks.ContainsKey(NetDisKConfig[2]))
                        {
                            isReady = RuntimeAdapter.NetDisks[NetDisKConfig[2]];
                        }
                        else
                        {
                            RuntimeAdapter.NetDisks.Add(NetDisKConfig[2], false);
                        }
                    }
                    
                    if (!isReady)
                    {
                        WriteWarnMessageExt(diskCfg.SerialNum, "缓存中未找到指定盘符，开始创建网络盘连接");
                        CreateNetDirectory(diskCfg.Controller.ResouceNetDiskConfig, diskCfg.SerialNum);
                    }
                }
            }
            
        }

        /// <summary>
        /// 创建本地网络盘符映射
        /// </summary>
        /// <param name="diskCfg">网络盘符配置</param>
        /// <param name="logTag">日志输出标签</param>
        /// <returns></returns>
        private bool CreateNetDirectory(string diskCfg, string logTag)
        {
            if (string.IsNullOrEmpty(diskCfg))
            {
                WriteErrMessageExt(logTag, "网络盘符参数配置异常");
                return false;
            }
            WriteDebugMessageExt(logTag, "当前资源网盘配置信息为：{0}", diskCfg);
            string[] NetDisKConfig = diskCfg.Split(',');
            if (NetDisKConfig.Length > 0)
            {
                uint state = 0;
                
                string tmpDir = NetDisKConfig[3];
                if (!Directory.Exists(tmpDir))
                {
                    WriteDebugMessageExt(logTag, "创建映射盘符{0}", tmpDir);
                    state = NetDiskHelper.WNetAddConnection(NetDisKConfig[0], NetDisKConfig[1], NetDisKConfig[2], tmpDir);
                }
                else
                {
                    WriteDebugMessageExt(logTag, "映射盘符{0}已存在，可以开始读写数据", tmpDir);
                }
                
                if (state.Equals(0))
                {
                    RuntimeAdapter.NetDisks[NetDisKConfig[2]] = true;
                    return true;
                }
                else if (state.Equals(1219))
                {
                    RuntimeAdapter.NetDisks[NetDisKConfig[2]] = true;
                    WriteErrMessageExt(logTag, "添加网络驱动器出现异常，异常编号为{0}", state);
                    //如果返回值为1219，表示已创建了链接，可不处理
                    //如要解决该报错，需先使用net use查看所有网络链接，然后使用net use \\172.25.8.78\IPC$ /del /y 进行删除
                    //ERROR_ALREADY_ASSIGNED 85 (0x55) The local device name is already in use. 本地设备名已经使用 
                    //ERROR_BAD_PROVIDER 1204 (0x4B4) The specified network provider name is invalid. 网络名无效
                    //ERROR_ALREADY_ASSIGNED 64 目标驱动器号已经被使用 
                    return true;
                }
                else
                {
                    //1312 是账号密码问题
                    WriteErrMessageExt(logTag, "添加网络驱动器错误，错误号：{0}" , state.ToString());
                    return false;
                }
            }
            else
            {
                WriteErrMessageExt(logTag, "映射盘符配置信息异常，请联系系统运维人员");
                return false;
            }
        }
        
        #endregion NetDisk

        /// <summary>
        /// 获取文件MD5码
        /// </summary>
        /// <param name="srcFile"></param>
        /// <returns></returns>
        private string GetFileMd5(string srcFile)
        {
            try
            {
                //LogHelper.WriteLog(typeof(C1Helper), srcFile);
                //文件规则：FTP根目录+EPG+年月+日+*.tar
                //string file = Path.Combine(Environment.CurrentDirectory, tarPath);
                if (!File.Exists(srcFile))
                {
                    //LogHelper.WriteLog(typeof(C1Helper), string.Format("资源文件{0}不存在", srcFile));
                    return "err";
                }
                return "";// Golemer.GetMd5WithFilePath(srcFile);
            }
            catch (Exception ex)
            {
                WriteDebugMessage(ex.ToString());
                return "err";
            }
        }

        /// <summary>
        /// 获取指定目录下的流格式文件
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private string GetM3U8FileFullPath(string path)
        {
            string[] tmpFiles = Directory.GetFiles(path, "*.m3u8");
            if (tmpFiles.Length > 0)
            {
                return tmpFiles[0];
            }
            else
            {
                return "";
            }
        }

        #region 任务响应
        private void NotifyTaskErr(string errStr, int errValue = (int)EnumRunItemStatu.Error)
        {
            WriteErrMessageExt(OutMessage.ID, "任务{0}执行失败, 错误信息为{1}", TaskID, errStr);
            waitSubThread = false;

            OutMessage.Code = errValue;
            OutMessage.Message = errStr;

            ShowErrMessage(errStr);
            TaskNotify(OutMessage);
        }

        /// <summary>
        /// 任务回执
        /// </summary>
        /// <param name="responseMsg">响应对象</param>
        private void TaskNotify(WorkOutMessage responseMsg)
        {
            try
            {
                TaskResponse returnTask = new TaskResponse()
                {
                    TaskID = this.TaskID,
                    TaskType = EnumRunTaskType.Down,
                    Statu = responseMsg.Code != 5 ? -1 : responseMsg.Code,
                    Comment = responseMsg.Message,
                    Token = responseMsg.Token,
                    ReturnValues = responseMsg
                };
                
                EndTopTask(TaskID, responseMsg.Code);

                //通过ToString方式进行序列化
                //string retContent = returnTask.ToString();
                string content = JsonAssistant.Serialize(returnTask);
                ShowDebugMessageExt(OutMessage.ID, "当前工单{0}提交回馈到{1}，内容为{2}", TaskID, RuntimeAdapter.WasNotifyUrl, JsonAssistant.SerializeAscii(returnTask));
                string rtnMsg = WebHelper.GetHttpResponse(RuntimeAdapter.WasNotifyUrl, "POST", content);
            }
            catch (Exception ex)
            {
                ShowErrMessage("任务{0}响应失败{1}", TaskID, ex.Message);
            }
            finally
            {
                this.Statu = (EnumRunItemStatu)responseMsg.Code;
            }
        }
        #endregion 任务响应

        /// <summary>
        /// 回调函数
        /// </summary>
        /// <param name="result"></param>
        private void JobCallBack(IAsyncResult result)
        {
            try
            {
                WriteDebugMessageExt(OutMessage.ID, "当前工作项 {0} 下载任务工作项正常完成流程操作，启动任务回调", TaskID);
                //获取代理方法
                GetHandler handler = (GetHandler)((AsyncResult)result).AsyncDelegate;
                //获取方法的结果
                bool rtn = handler.EndInvoke(result);

                OutMessage.Code = rtn ? (int)EnumRunItemStatu.End : (int)EnumRunItemStatu.Error;
                OutMessage.Message = rtn ? "工单资源下载成功" : string.IsNullOrEmpty(OutMessage.Message) ? "工单资源下载失败" : OutMessage.Message;
                OutMessage.CmdUrl = responsePlayUrl;

                TaskNotify(OutMessage);
            }
            catch (Exception ex)
            {
                WriteDebugMessage(ex.ToString());
            }
            this.Statu = EnumRunItemStatu.End;
        }

        #region 日志上报实现
        /// <summary>
        /// 日志上报实现
        /// </summary>
        /// <param name="head">工单流水号</param>
        /// <param name="msg">下载结果</param>
        /// <param name="type">日志类型</param>
        public void PushLog(string head, string msg, EnumLogLevel type)
        {
            try
            {
                if (RuntimeAdapter.AllowReportLog)
                {
                    //WriteDebugMessageExt(head, "启动{0}日志上报，内容为：{1}", type.ToString(), msg);
                    ShowDebugMessageExt(head, "启动{0}日志上报，内容为：{1}", type.ToString(), msg);
                    Loginfo log = new Loginfo() { SamName = RuntimeAdapter.HostName, LogType = type, Head = head, Message = msg };
                    string rtnMsg = WebHelper.GetHttpResponse(RuntimeAdapter.LogEngine, "POST", JsonAssistant.Serialize(log));
                }
            }
            catch (Exception ex)
            {
                ShowErrMessageExt(head, "日志上报异常：{0}", ex.ToString());
            }

        }
        #endregion 日志处理

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            if (_downQueueFiles != null)
            {
                _downQueueFiles.Clear();
                //_downQueueFiles = null;
            }
            this.Statu = EnumRunItemStatu.End;

            //if (_fileDownRecords != null)
            //{
            //    _fileDownRecords.Clear();
            //    _fileDownRecords = null;
            //}
        }
    }
}
